#include "../gfx/dlint.h"
#include "../core.h"

#include "../hle.h"

typedef struct
{
    u32 signal_func;
    u32 signal_arg;
    u32 finish_func;
    u32 finish_arg;
} PspGeCallbackData;

typedef enum
{
    PSP_GE_LIST_DONE = 0,
    PSP_GE_LIST_QUEUED,
    PSP_GE_LIST_DRAWING_DONE,
    PSP_GE_LIST_STALL_REACHED,
    PSP_GE_LIST_CANCEL_DONE
} PspGeSyncType;

void sceGeEdramGetAddr()
{
    u32 retVal = 0x04000000;
    _log(INF, HLE, "%08x = sceGeEdramGetAddr", retVal);
    RETURN(retVal);
}

void sceGeEdramGetSize()
{
    u32 retVal = 0x00200000;
    _log(INF, HLE, "%08x = sceGeEdramGetSize()", retVal);
    RETURN(retVal);
}

void sceGeEdramSetAddrTranslation()
{
    _log(ERR, HLE, "sceGeEdramSetAddrTranslation(%08x, %08x, %08x, %08x, %08x, %08x, ...)", PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5));
    RETURN(0);
}

void sceGeListEnQueue()
{
    u32 listAddress = PARAM(0);
    u32 stallAddress = PARAM(1);
    u32 callbackId = PARAM(2);
    u32 optParamAddr = PARAM(3);

    u32 listID;
    
    listID = dl_enqueue(listAddress, stallAddress);
    _log(INF, HLE, "%d = sceGeListEnQueue(addr = %08x, stall = %08x, cbid = %08x, param = %08x)",
                  listID, listAddress, stallAddress, callbackId, optParamAddr);
    /* return display list ID */
    RETURN(listID);
}

void sceGeListEnQueueHead()
{
    u32 listAddress = PARAM(0);
    u32 stallAddress = PARAM(1);
    u32 callbackId = PARAM(2);
    u32 optParamAddr = PARAM(3);

    u32 listID;

    listID = dl_enqueue(listAddress, stallAddress);
    _log(INF, HLE, "%d = sceGeListEnQueueHead(addr = %08x, stall = %08x, cbid = %08x, param = %08x)",
                 listID, listAddress, stallAddress, callbackId, optParamAddr);
    /* return display list ID */
    RETURN(listID);
}

void sceGeListUpdateStallAddr()
{
    u32 displayListID = PARAM(0);
    u32 stallAddress = PARAM(1);
    _log(INF, HLE, "sceGeListUpdateStallAddr(dlid = %d, stalladdr = %08x)", displayListID, stallAddress);
    dl_chStall(displayListID, stallAddress);
    RETURN(0);
}

void sceGeListSync()
{
    u32 displayListID = PARAM(0);
    u32 mode = PARAM(1); /* 0 : wait for completion    1:check and return */
    _log(INF, HLE, "sceGeListSync(dlid = %08x, mode = %08x)", displayListID, mode);
    switch (mode)
    {
    case PSP_GE_LIST_DONE:
#ifndef NO_THREAD
        if (core_inGfxThStack(GFXTH_DLINT)) {
            __KernelWaitCurThread(WAITTYPE_DRAWSYNC, 0, 0, 0, 0);
            core_drawSync();
        }
#endif
        break;
    case PSP_GE_LIST_QUEUED:
        RETURN(0);
        return; /* OK, DL are queued in the same thread as this one */
    case PSP_GE_LIST_DRAWING_DONE:
        _log(WRN, HLE, "- PSP_GE_LIST_DRAWING_DONE not implemented");
        break;
    case PSP_GE_LIST_STALL_REACHED:
        _log(WRN, HLE, "- PSP_GE_LIST_STALL_REACHED not implemented");
        break;
    case PSP_GE_LIST_CANCEL_DONE:
        _log(WRN, HLE, "- PSP_GE_LIST_CANCEL_DONE not implemented");
        break;
    }
}

void sceGeDrawSync()
{
    /* wait/check entire drawing state */
    u32 mode = PARAM(0); /* 0 : wait for completion    1:check and return */
    _log(INF, HLE, "sceGeDrawSync(mode = %d)", mode);
    if (mode == 0)
    {
#ifndef NO_THREAD
        if (core_inGfxThStack(GFXTH_DLINT)) {
            __KernelWaitCurThread(WAITTYPE_DRAWSYNC, 0, 0, 0, 0);
            core_drawSync();
        }
#endif
        RETURN(0);
    }
    else if (mode == 1)
        RETURN(0);
    else
        _log(ERR, HLE, "UNIMPL sceGeDrawSync() mode %d", mode);
}

void sceGeBreak()
{
    u32 mode = PARAM(0); /* 0 : current dlist 1: all drawing */
    _log(ERR, HLE, "sceGeBreak(mode = %d)", mode);
}

void sceGeContinue()
{
    _log(ERR, HLE, "sceGeContinue");
    /* no arguments */
}

void sceGeSetCallback() /* TODO: handle for more than one Ge callback set */
{
    u32 structAddr = PARAM(0);
    PspGeCallbackData cb;

    mem_readStruct(structAddr, &cb, sizeof(PspGeCallbackData));

    _log(INF, HLE, "sceGeSetCallback(struct = %08x):", structAddr);

    _log(INF, HLE, "- signal_func: %08x", cb.signal_func);
    if (cb.signal_arg)
        _log(INF, HLE, "- signal_arg: %08x (%08x)", cb.signal_arg, mem_read32(cb.signal_arg));
    else
        _log(INF, HLE, "- no signal_arg");

    if (mem_isOk(cb.signal_func)) {
        SceUID id = __KernelCreateCallback("GeCallbackSignal", cb.signal_func, cb.signal_arg);
        __KernelRegisterCallback(CBTYPE_GE_SIGNAL, id);
    }

    _log(INF, HLE, "- finish_func: %08x", cb.finish_func);
    if (cb.finish_arg)
        _log(INF, HLE, "- finish_arg: %08x (%08x)", cb.finish_arg, mem_read32(cb.finish_arg));
    else
        _log(INF, HLE, "- no finish_arg");

    if (mem_isOk(cb.finish_func)) {
        SceUID id = __KernelCreateCallback("GeCallbackFinish", cb.finish_func, cb.finish_arg);
        __KernelRegisterCallback(CBTYPE_GE_FINISH, id);
    }

    RETURN(0);
}

void sceGeUnsetCallback()
{
    u32 cbID = PARAM(0);
    _log(ERR, HLE, "sceGeUnsetCallback(cbid = %08x)", cbID);
}

